#include "ngx_config.h"
#include "ngx_http.h"
#include "ngx_core.h"
#include "ngx_md5.h"
#include "stdbool.h"
//#include "smtp.c"
#include <time.h>
#include "stdio.h"

#define FIRST_SERVER "/p0zv3kD1/"
#define SECOND_SERVER "/uuNmQSfk/"
#define THIRD_SERVER "/e34NfwKL/"
#define SERVER_NUM 3

#ifndef NGX_HTTP_MULTIPOST_MODULE_DEBUG
#define NGX_HTTP_MULTIPOST_MODULE_DEBUG
#endif

static char* ngx_http_multipost(ngx_conf_t* cf, ngx_command_t* cmd, void* conf);
static ngx_int_t ngx_http_multipost_handler(ngx_http_request_t* r);
static ngx_int_t multipost_subrequest_post_handler(ngx_http_request_t* r, void* data, ngx_int_t rc);
static ngx_int_t multipost_subrequest_post_handler2(ngx_http_request_t* r, void* data, ngx_int_t rc);
static ngx_int_t multipost_subrequest_post_handler3(ngx_http_request_t* r, void* data, ngx_int_t rc);
static void multipost_post_handler(ngx_http_request_t* r);
static ngx_int_t body_diff(ngx_str_t body1, ngx_str_t body2);
static void ngx_http_post_handler(ngx_http_request_t* r);
//static void saveIndexInFile(ngx_http_request_t *r, ngx_buf_t *buf);

//配置项数据结构
typedef struct {
    ngx_str_t receiver;
} ngx_http_multipost_conf_t;

//创建loc级数据结构
static void* ngx_http_multipost_create_conf(ngx_conf_t* cf)
{
    ngx_http_multipost_conf_t* mycf;
    mycf = (ngx_http_multipost_conf_t*)ngx_palloc(cf->pool, sizeof(ngx_http_multipost_conf_t));
    if (mycf == NULL)
        return NULL;
    return mycf;
}

static ngx_command_t ngx_http_multipost_commands[] = {
    { ngx_string("multipost"),
        NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_NOARGS,
        ngx_http_multipost, //ngx_http_multipost 函数负责接下来的过程
        NGX_HTTP_LOC_CONF_OFFSET,
        0,
        NULL },
    { ngx_string("receiver"), //收件人
        NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,
        ngx_conf_set_str_slot,
        NGX_HTTP_LOC_CONF_OFFSET,
        offsetof(ngx_http_multipost_conf_t, receiver),
        NULL },
    ngx_null_command
};

static ngx_http_module_t ngx_http_multipost_module_ctx = {
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    ngx_http_multipost_create_conf,
    NULL //ngx_http_multipost_merge_conf
};

ngx_module_t ngx_http_multipost_module = {
    NGX_MODULE_V1,
    &ngx_http_multipost_module_ctx,
    ngx_http_multipost_commands,
    NGX_HTTP_MODULE,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NGX_MODULE_V1_PADDING
};

typedef struct {
    ngx_str_t stock_info, stock_info_2, stock_info_3;
    int finished_subrequest_num;
    ngx_str_t content_type;
    ngx_http_request_t *sr1, *sr2, *sr3;
} ngx_http_multipost_ctx_t;

static char* ngx_http_multipost(ngx_conf_t* cf, ngx_command_t* cmd, void* conf)
{
    ngx_http_core_loc_conf_t* clcf;
    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
    clcf->handler = ngx_http_multipost_handler;
    return NGX_CONF_OK;
}

//接收客户请求时的方法
static ngx_int_t ngx_http_multipost_handler(ngx_http_request_t* r)
{

    //获取模块存储在ngx_http_request_t里的ctx数据
    ngx_http_multipost_ctx_t* myctx = ngx_http_get_module_ctx(r, ngx_http_multipost_module);
    if (myctx == NULL) {
        myctx = ngx_palloc(r->pool, sizeof(ngx_http_multipost_ctx_t));
        if (myctx == NULL)
            return NGX_ERROR;
        ngx_http_set_ctx(r, myctx, ngx_http_multipost_module);
    }

    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "!!!!!!!!!!!!!!uri get is: %V. ", &r->uri);

    //已完成子请求数设为0
    myctx->finished_subrequest_num = 0;

    //method必须为GET或POST
    if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD | NGX_HTTP_POST)))
        return NGX_HTTP_NOT_ALLOWED;

    if (r->method & NGX_HTTP_POST) { //如果是POST请求
        //读取请求头
        ngx_http_read_client_request_body(r, ngx_http_post_handler);
        return NGX_DONE;
    } else if (r->method & NGX_HTTP_GET) { //如果是GET请求
        ngx_str_t args = r->args;
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "ngx_http_multipost_handler args:%V.", &args);

        //以下创建第一个子请求
        ngx_http_post_subrequest_t* psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
        if (psr == NULL)
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        //绑定子请求handler
        psr->handler = multipost_subrequest_post_handler;
        //子请求数据
        psr->data = myctx;
        //请求uri
        ngx_str_t sub_prefix = ngx_string(FIRST_SERVER);
        ngx_str_t sub_location;
        sub_location.len = sub_prefix.len + r->uri.len;
        sub_location.data = ngx_palloc(r->pool, sub_location.len);

        ngx_snprintf(sub_location.data, sub_location.len, "%V%V", &sub_prefix, &r->uri);
#ifdef NGX_HTTP_MULTIPOST_MODULE_DEBUG
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "uri: %V. ", &r->uri);
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "request to first url: %V. ", &sub_location);
#endif

        ngx_http_request_t* sr;
        //第三个参数为请求参数，为空表示不传参
        ngx_int_t rc = ngx_http_subrequest(r, &sub_location, &args, &sr, psr, NGX_HTTP_SUBREQUEST_IN_MEMORY);
        if (rc != NGX_OK)
            return NGX_ERROR;

        //以下创建第二个子请求
        ngx_http_post_subrequest_t* psr2 = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
        if (psr2 == NULL)
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        psr2->handler = multipost_subrequest_post_handler2;
        psr2->data = myctx;
        ngx_str_t sub_prefix2 = ngx_string(SECOND_SERVER);
        ngx_str_t sub_location2;
        sub_location2.len = sub_prefix2.len + r->uri.len;
        sub_location2.data = ngx_palloc(r->pool, sub_location2.len);

        ngx_snprintf(sub_location2.data, sub_location2.len, "%V%V", &sub_prefix2, &r->uri);
#ifdef NGX_HTTP_MULTIPOST_MODULE_DEBUG
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "request to second url: %V. ", &sub_location2);
#endif

        ngx_http_request_t* sr2;
        ngx_int_t rc2 = ngx_http_subrequest(r, &sub_location2, &args, &sr2, psr2, NGX_HTTP_SUBREQUEST_IN_MEMORY);
        if (rc2 != NGX_OK)
            return NGX_ERROR;


        //以下创建第三个子请求
        ngx_http_post_subrequest_t* psr3 = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
        if (psr3 == NULL)
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        psr3->handler = multipost_subrequest_post_handler3;
        psr3->data = myctx;
        ngx_str_t sub_prefix3 = ngx_string(THIRD_SERVER);
        ngx_str_t sub_location3;
        sub_location3.len = sub_prefix3.len + r->uri.len;
        sub_location3.data = ngx_palloc(r->pool, sub_location3.len);

        ngx_snprintf(sub_location3.data, sub_location3.len, "%V%V", &sub_prefix3, &r->uri);
#ifdef NGX_HTTP_MULTIPOST_MODULE_DEBUG
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "request to third url: %V. ", &sub_location3);
#endif

        ngx_http_request_t* sr3;
        ngx_int_t rc3 = ngx_http_subrequest(r, &sub_location3, &args, &sr3, psr3, NGX_HTTP_SUBREQUEST_IN_MEMORY);
        if (rc3 != NGX_OK)
            return NGX_ERROR;

        return NGX_OK;
    }
    return NGX_OK;
}

// 请求包体读取完成后的回调函数
static void ngx_http_post_handler(ngx_http_request_t* r)
{
    ngx_http_multipost_ctx_t* myctx = ngx_http_get_module_ctx(r, ngx_http_multipost_module);
    ngx_str_t args = r->args;

    //以下创建第一个子请求
    ngx_http_post_subrequest_t* psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
    if (psr == NULL)
        return;
    //绑定子请求handler
    psr->handler = multipost_subrequest_post_handler;
    //子请求数据
    psr->data = myctx;
    //请求uri
    ngx_str_t sub_prefix = ngx_string(FIRST_SERVER);
    ngx_str_t sub_location;
    sub_location.len = sub_prefix.len + r->uri.len;
    sub_location.data = ngx_palloc(r->pool, sub_location.len);

    ngx_snprintf(sub_location.data, sub_location.len, "%V%V", &sub_prefix, &r->uri);
#ifdef NGX_HTTP_MULTIPOST_MODULE_DEBUG
    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "uri: %V. ", &r->uri);
    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "request to first url: %V. ", &sub_location);
#endif

    ngx_http_request_t* sr;
    //第三个参数为请求参数，为空表示不传参
    ngx_int_t rc = ngx_http_subrequest(r, &sub_location, &args, &sr, psr, NGX_HTTP_SUBREQUEST_IN_MEMORY);
    if (rc != NGX_OK)
        return;

    //以下创建另第二个子请求
    ngx_http_post_subrequest_t* psr2 = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
    if (psr2 == NULL)
        return;
    psr2->handler = multipost_subrequest_post_handler2;
    psr2->data = myctx;

    ngx_str_t sub_prefix2 = ngx_string(SECOND_SERVER);
    ngx_str_t sub_location2;
    sub_location2.len = sub_prefix2.len + r->uri.len;
    sub_location2.data = ngx_palloc(r->pool, sub_location2.len);

    ngx_snprintf(sub_location2.data, sub_location2.len, "%V%V", &sub_prefix2, &r->uri);
#ifdef NGX_HTTP_MULTIPOST_MODULE_DEBUG
    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "request to second url: %V. ", &sub_location2);
#endif

    ngx_http_request_t* sr2;
    ngx_int_t rc2 = ngx_http_subrequest(r, &sub_location2, &args, &sr2, psr2, NGX_HTTP_SUBREQUEST_IN_MEMORY);
    if (rc2 != NGX_OK)
        return;


    //以下创建第三个子请求
    ngx_http_post_subrequest_t* psr3 = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
    if (psr3 == NULL)
        return;
    psr3->handler = multipost_subrequest_post_handler3;
    psr3->data = myctx;

    ngx_str_t sub_prefix3 = ngx_string(THIRD_SERVER);
    ngx_str_t sub_location3;
    sub_location3.len = sub_prefix3.len + r->uri.len;
    sub_location3.data = ngx_palloc(r->pool, sub_location3.len);

    ngx_snprintf(sub_location3.data, sub_location3.len, "%V%V", &sub_prefix3, &r->uri);
#ifdef NGX_HTTP_MULTIPOST_MODULE_DEBUG
    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "request to third url: %V. ", &sub_location3);
#endif

    ngx_http_request_t* sr3;
    ngx_int_t rc3 = ngx_http_subrequest(r, &sub_location3, &args, &sr3, psr3, NGX_HTTP_SUBREQUEST_IN_MEMORY);
    if (rc3 != NGX_OK)
        return;
    
    sr->method = r->method;
    sr->method_name = r->method_name;
    sr2->method = r->method;
    sr2->method_name = r->method_name;
    sr3->method = r->method;
    sr3->method_name = r->method_name;
    //r->main->count++; //!!!may have some problems
    ngx_http_finalize_request(r, NGX_DONE);

    return;
}

//第一个子请求结束时的回调方法
static ngx_int_t multipost_subrequest_post_handler(ngx_http_request_t* r, void* data, ngx_int_t rc)
{
#ifdef NGX_HTTP_MULTIPOST_MODULE_DEBUG
    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "request to first handler called. ");
#endif
    ngx_http_request_t* pr = r->parent;
    ngx_http_multipost_ctx_t* myctx = ngx_http_get_module_ctx(pr, ngx_http_multipost_module);

    //如果已完成请求是2，代表请求已经完成了
    if (myctx->finished_subrequest_num >= SERVER_NUM) {
        return NGX_OK;
    }

    myctx->sr1 = r;
    //父请求返回状态设置为子请求的返回状态
    pr->headers_out.status = r->headers_out.status;

    //缓冲区数据赋值到myctx->stock_info中
    ngx_buf_t* pRecvBuf = &(r->upstream->buffer);
    myctx->stock_info.data = pRecvBuf->pos;
    myctx->stock_info.len = pRecvBuf->last - pRecvBuf->pos;

    //如果子请求返回状态是成功
    if (r->headers_out.status == NGX_HTTP_OK) {
#ifdef NGX_HTTP_MULTIPOST_MODULE_DEBUG
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "request to first success.content_type:%s ", r->headers_out.content_type.data);
#endif
        //设置响应内容类型
        myctx->content_type = r->headers_out.content_type;

    } else {
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "status:%d", r->headers_out.status);
    }

    //已完成子请求数+1，注意，这里是异步的，可能会产生线程同步问题
    myctx->finished_subrequest_num++;
    //子请求结束后，调用父请求回调方法
    pr->write_event_handler = multipost_post_handler;
    return NGX_OK;
}

//第二个子请求结束时的回调方法
static ngx_int_t multipost_subrequest_post_handler2(ngx_http_request_t* r, void* data, ngx_int_t rc)
{
#ifdef NGX_HTTP_MULTIPOST_MODULE_DEBUG
    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "request to second handler called. ");
#endif

    ngx_http_request_t* pr = r->parent;
    ngx_http_multipost_ctx_t* myctx = ngx_http_get_module_ctx(pr, ngx_http_multipost_module);

    if (myctx->finished_subrequest_num >= SERVER_NUM) {
        return NGX_OK;
    }
    myctx->sr2 = r;
    pr->headers_out.status = r->headers_out.status;

    ngx_buf_t* pRecvBuf = &(r->upstream->buffer);
    myctx->stock_info_2.data = pRecvBuf->pos;
    myctx->stock_info_2.len = pRecvBuf->last - pRecvBuf->pos;

    if (r->headers_out.status == NGX_HTTP_OK) {

#ifdef NGX_HTTP_MULTIPOST_MODULE_DEBUG
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "request to second success.content_type:%s ", r->headers_out.content_type.data);
#endif

        myctx->content_type = r->headers_out.content_type;

    } else {
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "status:%d", r->headers_out.status);
    }

    myctx->finished_subrequest_num++;
    pr->write_event_handler = multipost_post_handler;
    return NGX_OK;
}



//第三个子请求结束时的回调方法
static ngx_int_t multipost_subrequest_post_handler3(ngx_http_request_t* r, void* data, ngx_int_t rc)
{
#ifdef NGX_HTTP_MULTIPOST_MODULE_DEBUG
    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "request to third handler called. ");
#endif

    ngx_http_request_t* pr = r->parent;
    ngx_http_multipost_ctx_t* myctx = ngx_http_get_module_ctx(pr, ngx_http_multipost_module);

    if (myctx->finished_subrequest_num >= SERVER_NUM) {
        return NGX_OK;
    }
    myctx->sr3 = r;
    pr->headers_out.status = r->headers_out.status;

    ngx_buf_t* pRecvBuf = &(r->upstream->buffer);
    myctx->stock_info_3.data = pRecvBuf->pos;
    myctx->stock_info_3.len = pRecvBuf->last - pRecvBuf->pos;

    if (r->headers_out.status == NGX_HTTP_OK) {

#ifdef NGX_HTTP_MULTIPOST_MODULE_DEBUG
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "request to third success.content_type:%s ", r->headers_out.content_type.data);
#endif

        myctx->content_type = r->headers_out.content_type;

    } else {
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "status:%d", r->headers_out.status);
    }

    myctx->finished_subrequest_num++;
    pr->write_event_handler = multipost_post_handler;
    return NGX_OK;
}


//父请求回调方法
static void multipost_post_handler(ngx_http_request_t* r)
{

#ifdef NGX_HTTP_MULTIPOST_MODULE_DEBUG
    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "父请求回调方法被调用了。");
#endif

    ngx_http_multipost_ctx_t* myctx = ngx_http_get_module_ctx(r, ngx_http_multipost_module);
    if (myctx->finished_subrequest_num != SERVER_NUM) {
//两个请求没有全部完成
#ifdef NGX_HTTP_MULTIPOST_MODULE_DEBUG
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "子请求没有全部完成，已完成: %d, 期望完成: %d. ", myctx->finished_subrequest_num, SERVER_NUM);
#endif
        return;
    }

    //保存返回的第一个包体信息
    FILE* fp = fopen("/tmp/content_from_first.tmp", "w+");
    if (fp != NULL) {
        fwrite(myctx->stock_info.data, myctx->stock_info.len, 1, fp);
        fclose(fp);
    } else {
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "open file failed. ");
    }

    //保存返回的第二个包体信息
    fp = fopen("/tmp/content_from_second.tmp", "w+");
    if (fp != NULL) {
        fwrite(myctx->stock_info_2.data, myctx->stock_info_2.len, 1, fp);
        fclose(fp);
    } else {
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "open file failed. ");
    }

    //保存返回的第三个包体信息
    fp = fopen("/tmp/content_from_third.tmp", "w+");
    if (fp != NULL) {
        fwrite(myctx->stock_info_3.data, myctx->stock_info_3.len, 1, fp);
        fclose(fp);
    } else {
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "open file failed. ");
    }


    ngx_str_t lastResultStr;
    //择多处理
    ngx_int_t diff_12 = body_diff(myctx->stock_info, myctx->stock_info_2);
    if (diff_12 == 0) { //说明至少两个包结果一样，直接返回这个包
        lastResultStr = myctx->stock_info;
    } else {
        ngx_int_t diff_13 = body_diff(myctx->stock_info, myctx->stock_info_3);
        if (diff_13 == 0) {
            lastResultStr = myctx->stock_info;
        } else {
            lastResultStr = myctx->stock_info_2;
        }
    }

    // ngx_int_t diff_ret = body_diff(myctx->stock_info, myctx->stock_info_2);
    // ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "different bytes of body1 and body2 is %d ##", diff_ret);

    // //如果两个数据包不同
    // if (diff_ret != 0) {
    //     ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "1!=2");
    //     ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);
    //     return;
    // }
    // lastResultStr = myctx->stock_info;
    
    int bodylen = lastResultStr.len;
    r->headers_out.content_length_n = bodylen;
    //分配内存池
    ngx_buf_t* b = ngx_create_temp_buf(r->pool, bodylen);
    b->pos = lastResultStr.data;
    b->last = b->pos + bodylen;
    b->last_buf = 1;

    ngx_chain_t out;
    out.buf = b;
    out.next = NULL;
    //static ngx_str_t type = ngx_string("text/plain;");
    //r->headers_out.content_type = type;

    r->headers_out.content_type = myctx->content_type;
    r->headers_out.status = NGX_HTTP_OK;
    r->connection->buffered |= NGX_HTTP_WRITE_BUFFERED;
    ngx_int_t ret = ngx_http_send_header(r);
    ret = ngx_http_output_filter(r, &out);

#ifdef NGX_HTTP_MULTIPOST_MODULE_DEBUG
    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "response has been sent. ");
#endif

    ngx_http_finalize_request(r, ret);
    return;
}

/*
 * return -1 代表两个包长度不同
 * return >= 0 代表两个包不相同的字节数。
 */
static ngx_int_t body_diff(ngx_str_t body1, ngx_str_t body2)
{
    size_t i;
    ngx_int_t ret = 0;
    if (body1.len != body2.len)
        return -1;
    for (i = 0; i < body1.len; i++) {
        if (*(body1.data + i) != *(body2.data + i))
            ret++;
    }
    return ret;
}